home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
com
/
internet
/
stik
/
gluestik
/
transdmn.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-08-31
|
21KB
|
906 lines
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ioctl.h>
#include <fcntl.h>
#include <arpa/nameser.h> /* for <resolv.h> */
#define __const const /* grrrr... */
#include <resolv.h> /* for h_error */
#include <mintbind.h>
#include "global.h"
#include "pipe.h"
/* MiNTnet's fcntl(FIONREAD) returns this when the socket shuts down, or
an error condition occurs, or there's no more to read. */
#define NO_LIMIT 0x7FFFFFFFL
typedef struct CIB_chain {
int the_fd;
#ifndef BLOCK_OPEN
int established;
#endif
CIB the_CIB;
struct CIB_chain *next;
} CIB_chain;
Daemon_Op OP;
Daemon_Retval RET;
CIB_chain *CIB_list = 0;
static int daemon_pipe;
int init_net()
{
if (Psemaphore(0, DMN_SEMAPHORE, 0L) < 0) {
Cconws("Cannot get semaphore for daemon access\r\n");
return 0;
}
/* We own the semaphores when they're created; release them so we can
re-grab them later */
Psemaphore(3, DMN_SEMAPHORE, 0L);
#ifdef DEBUG
if (Psemaphore(0, DEBUG_SEMAPHORE, 0L) < 0) {
Cconws("Cannot get semaphore for debug log\r\n");
return 0;
}
Psemaphore(3, DEBUG_SEMAPHORE, 0L);
#endif
return 1;
}
void dispatch()
{
PMSG pmsg;
Daemon_Op *OP;
Daemon_Retval *RET;
int n;
for (;;) {
n = Pmsg(0, DMN_MBOX, &pmsg);
if (n < 0) {
exit(1);
}
OP = (Daemon_Op *)pmsg.userlong1;
RET = (Daemon_Retval *)pmsg.userlong2;
#ifdef DEBUG
debug("sdsd", "Received op ", OP->common.op, " from pid ", pmsg.pid);
#endif
switch (OP->common.op) {
case OP_TCP_OPEN:
RET->ret_int16 = do_TCP_open(OP->P_TCP_open.hostaddr,
OP->P_TCP_open.port, OP->P_TCP_open.tos,
OP->P_TCP_open.obsize);
break;
case OP_TCP_CLOSE:
RET->ret_int16 = do_TCP_close(OP->P_TCP_close.fd,
OP->P_TCP_close.timeout);
break;
case OP_TCP_SEND:
RET->ret_int16 = do_TCP_send(OP->P_TCP_send.fd, OP->P_TCP_send.buf,
OP->P_TCP_send.buflen);
break;
case OP_TCP_WAIT_STATE:
RET->ret_int16 = do_TCP_wait_state(OP->P_TCP_wait_state.fd,
OP->P_TCP_wait_state.state,
OP->P_TCP_wait_state.timeout);
break;
case OP_UDP_OPEN:
RET->ret_int16 = do_UDP_open(OP->P_UDP_open.hostaddr,
OP->P_UDP_open.port);
break;
case OP_UDP_CLOSE:
RET->ret_int16 = do_UDP_close(OP->P_UDP_close.fd);
break;
case OP_UDP_SEND:
RET->ret_int16 = do_UDP_send(OP->P_UDP_send.fd, OP->P_UDP_send.buf,
OP->P_UDP_send.buflen);
break;
case OP_CNBYTE_COUNT:
RET->ret_int16 = do_CNbyte_count(OP->P_CNbyte_count.fd);
break;
case OP_CNGET_CHAR:
RET->ret_int16 = do_CNget_char(OP->P_CNget_char.fd);
break;
case OP_CNGET_NDB:
RET->ret_NDBptr = do_CNget_NDB(OP->P_CNget_NDB.fd);
break;
case OP_CNGET_BLOCK:
RET->ret_int16 = do_CNget_block(OP->P_CNget_block.fd,
OP->P_CNget_block.buf,
OP->P_CNget_block.len);
break;
case OP_RESOLVE:
RET->ret_int16 = do_resolve(OP->P_resolve.hostname,
OP->P_resolve.realname,
OP->P_resolve.addrs,
OP->P_resolve.naddrs);
break;
case OP_CNGETINFO:
RET->ret_CIBptr = do_CNgetinfo(OP->P_CNgetinfo.fd);
break;
case OP_KRMALLOC:
RET->ret_charptr = do_KRmalloc(OP->P_KRmalloc.size);
break;
case OP_KRFREE:
do_KRfree(OP->P_KRfree.mem);
RET->ret_int32 = 0;
break;
case OP_KRGETFREE:
RET->ret_int32 = do_KRgetfree(OP->P_KRgetfree.flag);
break;
case OP_KRREALLOC:
RET->ret_charptr = do_KRrealloc(OP->P_KRrealloc.mem,
OP->P_KRrealloc.newsize);
break;
case OP_GETVSTR:
RET->ret_charptr = do_getvstr(OP->P_KRfree.mem);
break;
default:
#ifdef DEBUG
debug("sd", "in dispatch(): unrecognized opcode ", OP->common.op);
#endif
break;
}
#ifdef DEBUG
debug("sdsd", "Responding to op ", OP->common.op, " from pid ", pmsg.pid);
#endif
Pmsg(1, 0xFFFF0000L | pmsg.pid, &pmsg);
continue;
}
}
static int make_CIB(int fd, uint16 protocol, uint16 lport, uint16 rport,
uint32 rhost, int established)
{
CIB_chain *C;
int retval = 1;
C = (CIB_chain *)do_KRmalloc(sizeof(CIB_chain));
if (!C)
retval = 0;
else {
C->the_fd = fd;
#ifndef BLOCK_OPEN
C->established = established;
#endif
C->the_CIB.protocol = protocol;
C->the_CIB.lport = lport;
C->the_CIB.rport = rport;
C->the_CIB.rhost = rhost;
C->next = CIB_list;
CIB_list = C;
}
#ifdef DEBUG
debug("si", "make_CIB() returns ", retval);
#endif
return retval;
}
static CIB *find_CIB(int fd)
{
register CIB_chain *C;
CIB *CC = 0;
for (C = CIB_list; C; C = C->next) {
if (C->the_fd == fd) {
CC = &(C->the_CIB);
break;
}
}
#ifdef DEBUG
debug("sp", "find_CIB() returns ", (void *)CC);
#endif
return CC;
}
static void remove_CIB(int fd)
{
register CIB_chain *C = CIB_list, *C2;
if (C && C->the_fd == fd) {
CIB_list = C->next;
do_KRfree(C);
} else {
for (C = CIB_list; C && C->next; C = C->next) {
if (C->next->the_fd == fd) {
C2 = C->next;
C->next = C2->next;
do_KRfree(C2);
break;
}
}
}
#ifdef DEBUG
debug("s", "remove_CIB() done");
#endif
}
#ifndef BLOCK_OPEN
/* Blech blech blech. I really don't like having to keep state info
like this, but there appears to be no reliable way to get this info
on the fly. */
static int is_established(int fd)
{
register CIB_chain *C;
for (C = CIB_list; C; C = C->next) {
if (C->the_fd == fd) {
break;
}
}
#ifdef DEBUG
debug("sdsss", "Connection ", fd, " is ",
(C->established ? "" : "not "), "established");
#endif
return (C ? C->established : 0);
}
static void set_establish(int fd)
{
register CIB_chain *C;
for (C = CIB_list; C; C = C->next) {
if (C->the_fd == fd) {
C->established = 1;
break;
}
}
}
#endif /* BLOCK_OPEN */
/* Incompatibility: obsize is ignored. MiNTnet does its own buffer
handling. */
int16 do_TCP_open(uint32 hostaddr, int16 port, int16 tos, uint16 obsize)
{
struct sockaddr_in addr;
int fd;
size_t scratch = sizeof(struct sockaddr_in);
#ifdef DEBUG
debug("slslslslsisisis", "Entering TCP_open(", (hostaddr>>24), ".",
(hostaddr>>16)&0xFFL, ".", (hostaddr>>8)&0xFFL, ".", hostaddr&0xFFL,
", ", port, ", ", tos, ", ", obsize, ")");
#endif
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = hostaddr;
addr.sin_port = port;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
switch (errno) {
case EMFILE:
return E_NOCCB;
case ENOMEM:
return E_NOMEM;
case ENOENT:
case EAFNOSUPPORT:
case ESOCKTNOSUPPORT:
case EPROTONOSUPPORT:
case EPROTOTYPE:
return -1000 - errno;
default:
return E_CONNECTFAIL;
}
}
if (hostaddr != 0) {
#ifndef BLOCK_OPEN
/* From the STiK specs: "TCP_open() returns immediately, without
waiting for the connection to establish." */
if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
return E_CONNECTFAIL;
#endif
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
switch (errno) {
case EBADF:
case EISCONN:
return E_BADHANDLE;
case EINVAL:
return E_LISTEN;
case ETIMEDOUT:
return E_CNTIMEOUT;
case ECONNREFUSED:
return E_REFUSE;
case EALREADY:
case EINPROGRESS:
case EINTR:
break;
case EAFNOSUPPORT:
case EDESTADDRREQ:
case EOPNOTSUPP:
return -1000 - errno;
default:
return E_CONNECTFAIL;
}
}
/* Cobble up the CIB structure for this connection. The only thing
we don't yet have is the local port number. */
if (getsockname(fd, (struct sockaddr *)&addr, &scratch) < 0) {
int the_err = errno;
close(fd);
switch (the_err) {
case EBADF:
return E_BADHANDLE;
case EINVAL:
case EOPNOTSUPP:
default:
return -1000 - the_err;
}
}
if (!make_CIB(fd, P_TCP, addr.sin_port, port, hostaddr, 0)) {
close(fd);
return E_NOMEM;
}
#ifdef DEBUG
debug("si", "TCP_open() returns socket handle ", fd);
#endif
return fd;
} else {
int in_fd;
size_t addr_size = sizeof(addr);
/* From the STiK specs: "If rhost is 0, then the connection becomes
a LISTEN socket, and waits for a connection request from a remote
host." */
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
switch (errno) {
case EBADF:
case EADDRINUSE:
case EINVAL:
return E_BADHANDLE;
case EAFNOSUPPORT:
case EACCES:
case EDESTADDRREQ:
return -1000 - errno;
default:
return E_CONNECTFAIL;
}
}
if (listen(fd, 5) < 0) {
int the_err = errno;
close(fd);
switch (the_err) {
case EBADF:
return E_BADHANDLE;
case EDESTADDRREQ:
case EOPNOTSUPP:
case EINVAL:
return -1000 - the_err;
default:
return E_CONNECTFAIL;
}
}
/* Incompatibility: I don't think I can emulate STiK's non-blocking
LISTEN ports. STiK can apparently get away with this because it
replaces the listen()'ed socket with the accept()'ed socket
in situ; this behavior cannot be reproduced with Berkeley
sockets. Hopefully this won't be too much of a problem. */
if ((in_fd = accept(fd, (struct sockaddr *)&addr, &addr_size)) < 0) {
int the_err = errno;
close(fd);
switch (the_err) {
case EBADF:
return E_BADHANDLE;
case EMFILE:
return E_NOCCB;
case ENOMEM:
return E_NOMEM;
case EOPNOTSUPP:
case EWOULDBLOCK:
case ECONNABORTED:
case EINVAL:
case EINTR:
return -1000 - the_err;
default:
return E_CONNECTFAIL;
}
}
/* In STiK, an accept() apparently "eats" the listen()'ed socket; we
emulate that by closing it. */
close(fd);
/* Cobble up the CIB structure for this connection. The only thing
we don't yet have is the local port number. */
if (getsockname(in_fd, (struct sockaddr *)&addr, &scratch) < 0) {
int the_err = errno;
close(in_fd);
switch (the_err) {
case EBADF:
return E_BADHANDLE;
case EINVAL:
case EOPNOTSUPP:
default:
return -1000 - the_err;
}
}
if (!make_CIB(in_fd, P_TCP, addr.sin_port, port, hostaddr, 1)) {
close(in_fd);
return E_NOMEM;
}
return in_fd;
}
}
/* Incompatibility: timeout is ignored */
int16 do_TCP_close(int16 fd, int16 timeout)
{
int ret;
remove_CIB(fd);
ret = close(fd);
#ifdef DEBUG
debug("sisi", "TCP_close(", fd, ") returns ", (ret < 0 ? E_BADCLOSE : 0));
#endif
if (ret < 0)
return E_BADCLOSE;
else return 0;
}
/* Incompatibility: TCP_send() will probably be blocking; after all, I
can't return E_OBUFFULL in good faith if I don't honor obsize from
TCP_open(). */
/* Incompatibility: What is TCP_send() supposed to do if the non-blocking
TCP_open() hasn't finished? The STiK specs don't give a clue; I'm
gonna just turn off O_NDELAY and wait for the connection to establish. */
int16 do_TCP_send(int16 fd, char* buf, int16 buflen)
{
int n, tot = 0;
#ifdef DEBUG
debug("sSs", "in TCP_send(\"", buflen, buf, "\")");
#endif
#ifndef BLOCK_OPEN
if (!is_established(fd))
do_TCP_wait_state(fd, 0, 0);
#endif
for (;;) {
n = write(fd, buf + tot, buflen - tot);
if (n < 0) {
switch (errno) {
case EBADF:
return E_BADHANDLE;
case ENOSPC:
return E_NOMEM;
case ENXIO:
return E_LOSTCARRIER;
/* case EAGAIN: */
case ERANGE:
return E_OBUFFULL;
default:
return -1000 - errno;
}
} else {
tot += n;
if (tot >= buflen)
return E_NORMAL;
}
}
}
/* Incompatibility: Yeah, right, like I could reproduce the behavior of
this function... Just select() on the socket; this works for waiting
for the non-blocking open to complete, which is probably the only
thing it gets used for. */
int16 do_TCP_wait_state(int16 fd, int16 state, int16 timeout)
{
auto fd_set read_set, write_set, except_set;
struct timeval T;
int n;
#ifdef DEBUG
debug("s", "in TCP_wait_state()");
#endif
#ifndef BLOCK_OPEN
if (is_established(fd))
return E_NORMAL;
n = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, n & ~O_NDELAY);
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
FD_ZERO(&write_set);
FD_SET(fd, &write_set);
FD_ZERO(&except_set);
FD_SET(fd, &except_set);
T.tv_sec = timeout;
T.tv_usec = 0;
n = select(fd + 1, &read_set, &write_set, &except_set, &T);
if (n == 0)
return E_USERTIMEOUT;
if (n > 0) {
set_establish(fd);
return E_NORMAL;
}
switch (errno) {
case EBADF:
return E_BADHANDLE;
case EINTR:
case EINVAL:
default:
return -1000 - errno;
}
#else
return E_NORMAL;
#endif /* BLOCK_OPEN */
}
int16 do_UDP_open(uint32 hostaddr, int16 port)
{
struct sockaddr_in addr;
int fd;
size_t scratch = sizeof(struct sockaddr_in);
#ifdef DEBUG
debug("s", "in UDP_open()");
#endif
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = hostaddr;
addr.sin_port = port;
memset(addr.sin_zero, 0, 8);
fd = socket(AF_UNIX, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
switch (errno) {
case EMFILE:
return E_NOCCB;
case ENOMEM:
return E_NOMEM;
case ENOENT:
case EAFNOSUPPORT:
case ESOCKTNOSUPPORT:
case EPROTONOSUPPORT:
case EPROTOTYPE:
return -1000 - errno;
default:
return E_CONNECTFAIL;
}
}
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
int the_err = errno;
close(fd);
switch (the_err) {
case EBADF:
case EISCONN:
return E_BADHANDLE;
case EINVAL:
return E_LISTEN;
case ETIMEDOUT:
return E_CNTIMEOUT;
case ECONNREFUSED:
return E_REFUSE;
case EALREADY:
case EINPROGRESS:
case EINTR:
return fd;
case EAFNOSUPPORT:
case EDESTADDRREQ:
case EOPNOTSUPP:
return -1000 - the_err;
default:
return E_CONNECTFAIL;
}
}
/* Cobble up the CIB structure for this connection. The only thing
we don't yet have is the local port number. */
if (getsockname(fd, (struct sockaddr *)&addr, &scratch) < 0) {
int the_err = errno;
close(fd);
switch (the_err) {
case EBADF:
return E_BADHANDLE;
case EINVAL:
case EOPNOTSUPP:
default:
return -1000 - the_err;
}
}
if (!make_CIB(fd, P_UDP, addr.sin_port, port, hostaddr, 1)) {
close(fd);
return E_NOMEM;
}
return fd;
}
int16 do_UDP_close(int16 fd)
{
int ret;
#ifdef DEBUG
debug("s", "in UDP_close()");
#endif
remove_CIB(fd);
ret = close(fd);
if (ret < 0)
return E_BADCLOSE;
else return 0;
}
int16 do_UDP_send(int16 fd, char* buf, int16 buflen)
{
int n, tot = 0;
#ifdef DEBUG
debug("s", "in UDP_send()");
#endif
n = write(fd, buf + tot, buflen - tot);
if (n < 0) {
switch (errno) {
case EBADF:
return E_BADHANDLE;
case ENOSPC:
return E_NOMEM;
case ENXIO:
return E_LOSTCARRIER;
case ERANGE:
return E_OBUFFULL;
default:
return -1000 - errno;
}
} else {
return E_NORMAL;
}
}
/* This is just an estimate, but it should work. */
int16 do_CNbyte_count(int16 fd)
{
long l;
if (fcntl(fd, FIONREAD, &l) < 0)
return -1000 - errno;
if (l == NO_LIMIT) {
#ifndef BLOCK_OPEN
/* Tricky. This could be either EOF or the connection not
established. In the latter case, we have the "0 or E_NODATA?"
puzzler from below. */
if (!is_established(fd)) {
return 0;
}
#endif
return E_EOF;
}
/* Now here's a puzzler. If there's no data available should I return
0 or E_NODATA? Dan Ackerman's mailer assumes the former, so that's
what I'll do. */
if (l == 0) {
/* Here's a sneaky trick: If there's no data, surrender the rest of
our timeslice so that the STiK app doesn't eat all CPU time
spinning its wheels. */
Syield();
return 0;
}
#ifdef DEBUG
debug("sdsl", "CNbyte_count(", fd, ") returns ", l);
#endif
#ifndef BLOCK_OPEN
/* I really don't like doing this here, since this function will
likely be called in a tight loop, but it has to be done, and this
is about the only place I can do it. */
set_establish(fd);
#endif
return l;
}
/* There should probably be a more efficient way of doing this than
calling read() */
int16 do_CNget_char(int16 fd)
{
char c;
int n;
n = read(fd, &c, 1);
if (n > 0) {
#ifndef BLOCK_OPEN
/* Again, I don't really like doing this here, but I don't think I
can avoid it. */
set_establish(fd);
#endif
#ifdef DEBUG
debug("sdscsxs", "CNget_char(", fd, ") returns '", c, "' (0x",
(unsigned)c, ")");
#endif
return c;
}
if (n == 0) {
#ifdef DEBUG
debug("sds", "CNget_char(", fd, ") returns EOF");
#endif
return E_EOF;
}
#ifdef DEBUG
debug("sdsd", "CNget_char(", fd, ") gets error code ", errno);
#endif
switch (errno) {
case EWOULDBLOCK:
return E_NODATA;
case EBADF:
return E_BADHANDLE;
default:
return -1000 - errno;
}
}
/* We fake this by reading whatever is there and building a a fake NDB
for it */
NDB* do_CNget_NDB(int16 fd)
{
int bufsize = do_CNbyte_count(fd);
char *buf;
NDB *ndb = 0;
if (bufsize <= 0) {
return 0;
}
buf = do_KRmalloc(bufsize);
if (!buf) {
return 0;
}
ndb = (NDB *)do_KRmalloc(sizeof(NDB));
if (!ndb) {
do_KRfree(buf);
return 0;
}
bufsize = read(fd, buf, bufsize);
if (bufsize <= 0) {
do_KRfree(buf);
do_KRfree((char *)ndb);
return 0;
}
ndb->ptr = ndb->ndata = buf;
ndb->len = bufsize;
ndb->next = 0;
#ifdef DEBUG
debug("sdsss", "CNget_NDB(", fd, ") returns |", ndb->ndata, "|");
#endif
return ndb;
}
/* Incompatibility: CNget_block() is blocking, because there's no way
to say to MiNTnet, "If you can't fill the entire buffer in one try,
don't read anything" */
int16 do_CNget_block(int16 fd, char* buf, int16 len)
{
int n, tot = 0;
#ifndef BLOCK_OPEN
if (!is_established(fd))
do_TCP_wait_state(fd, 0, 0);
#endif
for (;;) {
n = read(fd, buf + tot, len - tot);
if (n > 0) {
tot += n;
if (tot >= len) {
#ifdef DEBUG
debug("sisisss", "CNget_block(", fd, ", ", len, ") reads |", buf,
"|");
#endif
return tot;
}
} else if (n == 0) {
/* Incompatibility: If we read part of the buffer before getting
the EOF, there's no way to "unread" the read data. I'm torn
between returning the number of bytes actually read and
trusting the caller to notice, or returning E_EOF and
discarding the partial buffer. For now, I'll do the former. */
return tot;
} else {
switch (errno) {
case EWOULDBLOCK:
return E_NODATA;
case EBADF:
return E_BADHANDLE;
default:
return -1000 - errno;
}
}
}
}
int16 do_resolve(char *hostname, char **realname, uint32 *addrs, int16 naddrs)
{
register struct hostent *H = 0;
register char **raddr;
register int i;
#ifdef DEBUG
debug("ssspspsis", "in resolve(\"", hostname, "\", ", (void *)realname,
", ", (void *)addrs, ", ", naddrs, ")");
#endif
H = gethostbyname(hostname);
if (!H) {
#ifdef DEBUG
debug("s", "gethostbyname() returns NULL");
#endif
switch (h_errno) {
case HOST_NOT_FOUND:
return E_NOHOSTNAME;
case NO_DATA:
return E_DNSNOADDR;
case TRY_AGAIN:
case NO_RECOVERY:
default:
return E_CANTRESOLVE;
}
}
#ifdef DEBUG
debug("sp", " gethostbyname() returns ", (void *)H);
#endif
if (realname) {
#ifdef DEBUG
debug("sss", " copying name \"", H->h_name, "\"");
#endif
*realname = do_KRmalloc(strlen(H->h_name) + 1);
strcpy(*realname, H->h_name);
}
/* BUG: assumes addresses returned have type struct in_addr */
for (i = 0, raddr = H->h_addr_list; *raddr && i < naddrs; i++, raddr++) {
#ifdef DEBUG
debug("sXsi",
" copying address ", ((struct in_addr *)(*raddr))->s_addr,
" to array elmt ", i);
#endif
addrs[i] = ((struct in_addr *)(*raddr))->s_addr;
}
#ifdef DEBUG
debug("si", " returning ", i);
#endif
return i;
}
/* (possible) Incompatibility: It is possible, at least in theory, for
find_CIB() to return NULL; the STiK specs seem to imply that this
cannot happen, so the receiving app probably doesn't check for it.
In practice, find_CIB() should not return NULL, except for file
descriptors that aren't sockets opened by GlueSTiK. */
CIB* do_CNgetinfo(int16 fd)
{
#ifdef DEBUG
debug("s", "in do_CNgetinfo()");
#endif
return find_CIB(fd);
}